const flow = require('./flow')
const throwNewError = require('./throwError')
const db = require('../db')
const path = require('path')
const ExcelJS = require('exceljs')
const fs = require('fs');
const crypto = require('crypto');
// const chalk = require('chalk')
const { createLogger, format, transports } = require('winston');

const logger = createLogger({
    level: 'info',
    format: format.combine(
        format.timestamp(),
        format.printf(({ timestamp, level, message }) => {
            return `${timestamp} ${level}: ${message}`;
        })
    ),
    transports: [
        new transports.Console(), // 这是将日志输出到控制台的传输器
        new transports.File({ filename: 'combined.log' })
    ]
});

// methods
const methods = {
    user: () => { return require('../methods/user') }
};

// modules
const modules = {
    
};

async function getChalk() {
    return await import('chalk')
}

module.exports = {
    cLog: {
        enable: true,
        async api(exp, method, auth, status, data = {}) {
            if (this.enable === false) return
            let chalk = await getChalk()
            if (status == 1) {
                return
                logger.info(`\t${chalk.default.yellow('接口开始')} ${chalk.default[method == 'get' ? 'red' : 'blue'](method == 'get' ? 'GET' : 'PST')} ${exp + (() => {
                    let re = ''
                    for (let i = 0; i < 20 - exp.length * 2; i++) {
                        re += ' '
                    }
                    return re
                })()}\t ${data.currentUserName === undefined ? '未登录' : data.currentUserName}${data.currentUserId === undefined ? '' : `(${data.currentUserId})`}`)
            }
            if (status == 2) {
                logger.info(`\t${chalk.default.green('接口成功')} ${chalk.default[method == 'get' ? 'red' : 'blue'](method == 'get' ? 'GET' : 'PST')} ${exp + (() => {
                    let re = ''
                    for (let i = 0; i < 20 - exp.length * 2; i++) {
                        re += ' '
                    }
                    return re
                })()}\t ${data.currentUserName === undefined ? '未登录' : data.currentUserName}${data.currentUserId === undefined ? '' : `(${data.currentUserId})`}`)
            }
            if (status == 0) {
                logger.info(`\t${chalk.default.red('接口失败')} ${chalk.default.red(method == 'get' ? 'GET' : 'PST')} ${exp + (() => {
                    let re = ''
                    for (let i = 0; i < 20 - exp.length * 2; i++) {
                        re += ' '
                    }
                    return re
                })()}\t ${data.currentUserName === undefined ? '未登录' : data.currentUserName}`)
            }
        },
        async runner(status) {
            return
            if (this.enable === false) return
            let chalk = await getChalk()
            if (status) {
                logger.info(`${chalk.default.yellow('runner开始')}`)
            } else {
                logger.info(`${chalk.default.red('runner停止')}`)
            }
        },
        async db(sql, status) {
            return
            if (this.enable === false) return
            // return 
            let chalk = await getChalk()
            if (status == 1) {
                logger.info(`\t\t${chalk.default.yellow('数据库开始')} ${sql}`)
            }
            if (status == 2) {
                logger.info(`\t\t${chalk.default.green('数据库成功')} ${sql}`)
            }
            if (status == 0) {
                logger.info(`\t\t${chalk.default.red('数据库错误')} ${sql}`)
            }
        },
        async dbCacheGet(sql, status) {
            return
            if (this.enable === false) return
            let chalk = await getChalk()
            if (status == 1) {
                logger.info(`\t\t${chalk.default.yellow('取缓存开始')} ${sql}`)
            }
            if (status == 2) {
                logger.info(`\t\t${chalk.default.green('取缓存成功')} ${sql}`)
            }
            if (status == 0) {
                logger.info(`\t\t${chalk.default.red('取缓存失败')} ${sql}`)
            }
        },
        async dbCacheAdd(sql) {
            return
            if (this.enable === false) return
            let chalk = await getChalk()
            logger.info(`\t\t${chalk.default.green('添加缓存')} ${sql}`)
        },
        async userLogin(userData) {
            let chalk = await getChalk()
            logger.info(`\t${chalk.default.bgGreenBright('用户登录')}     \t\t\t\t ${userData.username}(${userData.id})`)
        },
        async frontStatus(username, userId, content) {
            let chalk = await getChalk()
            logger.info(`\t${chalk.default.green('前端')} ${username} ${userId} ${chalk.default.bgBlueBright(content)}`)
        }
    },
    getChalk,
    logger,
    methods,
    modules,
    cachef() { return require('../tools/cache'); },
    db,
    dbf() {
        return require('../db')
    },
    array: {
        delete: {
            byIndex(arr = [], index = -1) {
                if (check.isArray(arr) == false) {
                    check.throwError('arr应为数组')
                }
                if (check.isInt(index) == false) {
                    check.throwError('index应为整数')
                }
                return [...arr.slice(0, index), ...arr.slice(index + 1)]
            },
            byKeyValue(arr = [], key = '', value = undefined) {
                if (check.isArray(arr) == false) {
                    check.throwError('arr应为数组')
                }
                return arr.filter(item => item[key] != value)
            },
            byValue(arr = [], value = undefined) {
                if (check.isArray(arr) == false) {
                    check.throwError('arr应为数组')
                }
                return arr.filter(item => item != value)
            },
            clearUndefined(arr = []) {
                if (check.isArray(arr) == false) {
                    check.throwError('arr应为数组')
                }
                return arr.filter(item => item != undefined)
            }
        },
        async format(arr = [], func = async (e) => { return e }) {
            if (check.isArray(arr) == false) {
                check.throwError('arr应为数组')
            }
            for (let i = 0; i < arr.length; i++) {
                arr[i] = await func(arr[i])
            }
            return arr
        },
        count(arr, value) {
            let re = 0
            for (let i = 0; i < arr.length; i++) {
                if (arr[i] === value) re++
            }
            return re
        },
        getFirstIndex: {
            byKeyValue(arr = [], key = '', value = undefined) {
                for (let i = 0; i < arr.length; i++) {
                    if (arr[i][key] === value) return i
                }
                return -1
            }
        }
    },
    object: {
        async for(obj = {}, func = async (key, value) => { return value }) {
            for (let i = 0; i < Object.keys(obj).length; i++) {
                obj[Object.keys(obj)[i]] = await func(Object.keys(obj)[i], obj[Object.keys(obj)[i]])
            }
            return obj
        }
    },
    string: {
        delete: {
            bySubString(string, subString) {
                string = string.split(subString)
                let re = ''
                for (let i = 0; i < string.length; i++) {
                    re += string[i]
                }
                return re
            }
        }
    },
    JSON: {
        parse(string) {
            try {
                return JSON.parse(string)
            } catch (error) {
                return string
            }
        },
        stringify(obj) {
            return JSON.stringify(obj)
        }
    },
    sliceByPageSize(arr, page, size) {
        return arr.slice((page - 1) * size, page * size)
    },
    encodeString(string) {

        function isLetter(code) {
            if (code >= 65 && code <= 90) {
                return 1
            }
            if (code >= 97 && code <= 122) {
                return 1
            }
            if (code > 90 && code < 97) {
                return 2
            }
            if (code < 65) {
                return 0
            }
            return 2
        }

        function getNumberString(n, x) {
            n = JSON.stringify(n)
            for (let i = n.length; i < x; i++) {
                n = '0' + n
            }
            return n
        }

        string = string.split('')
        let string1 = ''
        for (let i = 0; i < string.length; i++) {
            string1 += JSON.stringify(JSON.stringify(string[i].charCodeAt(0)).length) + JSON.stringify(string[i].charCodeAt(0))
        }
        string1 = string1.split('')
        for (let i = 0; i < string1.length; i++) {
            if (string1[i] == '0') {
                continue
            }
            let c = i
            let ss = string1[c]
            while (true) {
                let cd = isLetter(parseInt(ss))
                if (cd == 2) {
                    break
                }
                if (cd == 1) {
                    string1[c] = String.fromCharCode(parseInt(ss))
                    for (let i1 = i; i1 < c; i1++) {
                        string1[i1] = '-'
                    }
                    i = c
                    break
                }
                c++
                if (c == string1.length) {
                    break
                }
                ss += string1[c]
            }
        }
        for (let i = 0; i < string1.length - 1; i++) {
            if (string1[i] == '2' && string1[i + 1] == '2') {
                string1[i] = '?'
                string1[i + 1] = '-'
            }
        }
        let string2 = []
        for (let i = 0; i < string1.length; i++) {
            if (string1[i] == '-') {
                continue
            }
            string2.push(JSON.stringify((string1[i].charCodeAt(0) - string1[i].charCodeAt(0) % 52) / 52) + getNumberString(string1[i].charCodeAt(0) % 52, 2))
        }
        let string3 = ''
        for (let i = string2.length - 1; i >= 0; i--) {
            string3 += string2[i]
        }
        string3 = string3.split('')
        let string4 = []
        for (let i = 0; i < string3.length; i += 2) {
            let ch = string3[i]
            if (i + 1 != string3.length) {
                ch += string3[i + 1]
            }
            if (parseInt(ch) < 52 && ch.length == 2) {
                if (parseInt(ch) < 26) {
                    string4.push(String.fromCharCode(parseInt(ch) + 65))
                } else {
                    string4.push(String.fromCharCode(parseInt(ch) + 97 - 26))
                }
            } else {
                string4.push(ch)
            }
        }
        let string5 = ''
        for (let i = 0; i < string4.length; i++) {
            string5 += string4[i]
        }
        return string5
    },
    decodeString(string) {
        function getNumberString(n, x) {
            n = JSON.stringify(n)
            for (let i = n.length; i < x; i++) {
                n = '0' + n
            }
            return n
        }

        string = string.split('')
        for (let i = 0; i < string.length; i++) {
            if (string[i].charCodeAt(0) >= 48 && string[i].charCodeAt(0) <= 56) {
                continue
            }
            if (string[i].charCodeAt(0) <= 90) {
                string[i] = getNumberString(string[i].charCodeAt(0) - 65, 2)
            } else {
                string[i] = getNumberString(string[i].charCodeAt(0) - 97 + 26, 2)
            }
        }
        let string2 = ''
        for (let i = 0; i < string.length; i++) {
            if (string[i] == '-8') {
                string2 += '9'
                continue
            }
            string2 += string[i]
        }
        string2 = string2.split('')
        let string3 = []
        for (let i = 0; i < string2.length; i += 3) {
            string3.push(getNumberString(parseInt(string2[i]) * 52 + parseInt(string2[i + 1] + string2[i + 2]), 3))
        }
        let string4 = []
        for (let i = string3.length - 1; i >= 0; i--) {
            string4.push(String.fromCharCode(parseInt(string3[i])))
        }
        let string5 = ''
        for (let i = 0; i < string4.length; i++) {
            if (string4[i] == '?') {
                string5 += '22'
                continue
            }
            if ((string4[i].charCodeAt(0) >= 48 && string4[i].charCodeAt(0) <= 57) == false) {
                string5 += JSON.stringify(string4[i].charCodeAt(0))
                continue
            }
            string5 += string4[i]
        }
        string5 = string5.split('')
        let string6 = ''
        for (let i = 0; i < string5.length; i++) {
            let n = parseInt(string5[i])
            let s = ''
            for (let i1 = 0; i1 < n; i1++) {
                s += string5[i + 1 + i1]
            }
            string6 += String.fromCharCode(parseInt(s))
            i += n
        }
        return string6
    },
    formatDate2(date) {
        const year = date.getFullYear();
        const month = (date.getMonth() + 1).toString().padStart(2, '0');  // 月份从0开始，+1调整
        const day = date.getDate().toString().padStart(2, '0');
        const hours = date.getHours().toString().padStart(2, '0');
        const minutes = date.getMinutes().toString().padStart(2, '0');
        const seconds = date.getSeconds().toString().padStart(2, '0');

        return `${year}${month}${day}${hours}${minutes}${seconds}`;
    },
    formatDate3(date) {
        const year = date.getFullYear();
        const month = (date.getMonth() + 1).toString().padStart(2, '0');  // 月份从0开始，+1调整
        const day = date.getDate().toString().padStart(2, '0');
        const hours = date.getHours().toString().padStart(2, '0');
        const minutes = date.getMinutes().toString().padStart(2, '0');
        const seconds = date.getSeconds().toString().padStart(2, '0');

        return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
    },
    formatTimeNew(inputTime = new Date()) {
        inputTime = new Date(inputTime)
        function getPeriod(hours) {
            if (hours >= 0 && hours < 6) {
                return '凌晨';
            } else if (hours >= 6 && hours < 11) {
                return '上午';
            } else if (hours >= 11 && hours < 14) {
                return '中午';
            } else if (hours >= 14 && hours < 18) {
                return '下午';
            } else {
                return '晚上';
            }
        }

        function getWeekName(n) {
            switch (n) {
                case 0:
                    return '星期天'
                case 1:
                    return '星期一'
                case 2:
                    return '星期二'
                case 3:
                    return '星期三'
                case 4:
                    return '星期四'
                case 5:
                    return '星期五'
                case 6:
                    return '星期六'
            }
        }
        let currentTime = new Date()
        let oneDaySecond = 86400000
        let currentDayStart = new Date(currentTime.getTime() - currentTime.getHours() * 60 * 60 * 1000 - currentTime
            .getMinutes() * 60 * 1000 - currentTime.getSeconds() * 1000)
        let lastDayStart = new Date(currentTime.getTime() - currentTime.getHours() * 60 * 60 * 1000 - currentTime
            .getMinutes() * 60 * 1000 - currentTime.getSeconds() * 1000 - oneDaySecond)
        let nextDayStart = new Date(currentTime.getTime() - currentTime.getHours() * 60 * 60 * 1000 - currentTime
            .getMinutes() * 60 * 1000 - currentTime.getSeconds() * 1000 + oneDaySecond)
        let nextDayEnd = new Date(currentTime.getTime() - currentTime.getHours() * 60 * 60 * 1000 - currentTime
            .getMinutes() * 60 * 1000 - currentTime.getSeconds() * 1000 + oneDaySecond * 2)
        let prefix = ''
        if (inputTime >= currentDayStart && inputTime < nextDayStart) {
            prefix = '今天'
        }
        if (inputTime >= lastDayStart && inputTime < currentDayStart) {
            prefix = '昨天'
        }
        if (inputTime >= nextDayStart && inputTime < nextDayEnd) {
            prefix = '明天'
        }
        if (prefix == '') {
            prefix = (inputTime.getFullYear() == currentTime.getFullYear() ? '' : inputTime.getFullYear().toString() +
                '年') + (inputTime.getMonth() + 1).toString() + '月' + inputTime.getDate() + '日 '
        }
        prefix += getPeriod(inputTime.getHours()) + ' '
        prefix += inputTime.getHours().toString() + ':'
        if (inputTime.getMinutes() < 10) {
            prefix += '0'
        }
        prefix += inputTime.getMinutes().toString() + ' '
        prefix += getWeekName(inputTime.getDay())
        return prefix
    },
    wait(n) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                return resolve()
            }, n * 1000);
        })
    },
    flow,
    throwNewError,
    deepCopy(o) {
        return this.JSON.parse(this.JSON.stringify(o))
    },
    formatDateString(inputDate) {
        const date = new Date(inputDate);
        const now = new Date();
        const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
        const target = new Date(date.getFullYear(), date.getMonth(), date.getDate());
        const diffDays = Math.round((target - today) / (1000 * 60 * 60 * 24));

        if (diffDays === 2) return "后天";
        if (diffDays === 1) return "明天";
        if (diffDays === 0) return "今天";
        if (diffDays === -1) return "昨天";
        if (diffDays === -2) return "前天";

        const weekdays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
        const targetWeekday = weekdays[date.getDay()];

        const oneWeekAgo = new Date();
        oneWeekAgo.setDate(today.getDate() - 7);

        const oneWeekLater = new Date();
        oneWeekLater.setDate(today.getDate() + 7);

        // if (target <= oneWeekAgo && target < today) return `上${targetWeekday}`;
        // if (target > today && target <= oneWeekLater) return targetWeekday;

        const year = date.getFullYear();
        const month = date.getMonth() + 1;
        const day = date.getDate();

        if (year === now.getFullYear()) {
            return `${month}月${day}日`;
        } else {
            return `${year}年${month}月${day}日`;
        }
    },
    formatTimeDifference(time1, time2) {
        let str = ''

        let month = 0
        let week = 0
        let day = 0
        let hour = 0
        let minute = 0

        let diff = Math.abs(new Date(time1) - new Date(time2)); // 计算时间差的绝对值

        let one_month = 1000 * 60 * 60 * 24 * 30
        let one_week = 1000 * 60 * 60 * 24 * 7
        let one_day = 1000 * 60 * 60 * 24
        let one_hour = 1000 * 60 * 60
        let one_minute = 1000 * 60

        month = parseInt(diff / one_month)
        diff -= one_month * month

        week = parseInt(diff / one_week)
        diff -= one_week * week

        day = parseInt(diff / one_day)
        diff -= one_day * day

        hour = parseInt(diff / one_hour)
        diff -= one_hour * hour

        minute = parseInt(diff / one_minute)
        diff -= one_minute * minute
        let re = `${month ? `${month}月` : ''}${week ? `${week}周` : ''}${day ? `${day}日` : ''}${hour ? `${hour}时` : ''}${minute ? `${minute}分钟` : ''}`
        return re == '' ? '1分钟内' : re

        // console.log(diff)

        let minutes = Math.floor(diff / (1000 * 60)) % 60;
        let hours = Math.floor(diff / (1000 * 60 * 60)) % 24;
        let days = Math.floor(diff / (1000 * 60 * 60 * 24)) % 7;
        let weeks = Math.floor(diff / (1000 * 60 * 60 * 24 * 7)) % 4;
        let months = Math.floor(diff / (1000 * 60 * 60 * 24 * 30));

        let result = [];
        if (months > 0) result.push(`${months}月`);
        if (weeks > 0) result.push(`${weeks}周`);
        if (days > 0) result.push(`${days}天`);
        if (hours > 0) result.push(`${hours}小时`);
        if (minutes > 0) result.push(`${minutes}分钟`);

        return result.length ? result.join('') : '0分钟';
    },
    getFullPathInfo(fullPath, maxDeep = -1) {
        const getFileInfo = (filePath, deep) => {
            let stats;
            try {
                stats = fs.statSync(filePath);
            } catch (error) {
                // 如果发生错误，直接跳过此文件或目录
                if (error.code === 'EPERM' || error.code === 'ENOENT') {
                    return null;
                } else {
                    throw error;
                }
            }

            const isDirectory = stats.isDirectory();

            const fileInfo = {
                isDir: isDirectory,
                name: path.basename(filePath),
                baseName: path.basename(filePath, path.extname(filePath)), // 去掉扩展名的文件名
                parentPath: path.dirname(filePath),
                fullPath: filePath,
                createdAt: stats.birthtime,
                updatedAt: stats.mtime,
                createdAt_read: tool.formatTimeNew(stats.birthtime),
                updatedAt_read: tool.formatTimeNew(stats.mtime),
            };

            if (isDirectory) {
                if ((maxDeep > -1 && deep <= maxDeep) || maxDeep == -1) {
                    const children = fs.readdirSync(filePath)
                        .map(child => getFileInfo(path.join(filePath, child), deep + 1))
                        .filter(child => child !== null) // 过滤掉无法访问的文件或目录
                        .sort((a, b) => {
                            if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
                            return b.updatedAt - a.updatedAt;
                        });
                    fileInfo.children = children;
                }
            } else {
                fileInfo.size = stats.size;
                fileInfo.size_read = this.formatSize(stats.size);
                fileInfo.extension = path.extname(filePath).slice(1); // 获取文件扩展名，并去掉开头的点
            }

            return fileInfo;
        };

        return getFileInfo(fullPath, 1);
    },
    formatSize(bytes) {
        const units = ['B', 'KB', 'MB', 'GB', 'TB'];
        let index = 0;
        let size = bytes;
        while (size >= 1024 && index < units.length - 1) {
            size /= 1024;
            index++;
        }
        return `${size.toFixed(2)} ${units[index]}`;
    },
    flatPathObject(pathObjectArray) {
        while (1) {
            let flag = false
            for (let i = 0; i < pathObjectArray.length; i++) {
                if (pathObjectArray[i] instanceof Array) {
                    pathObjectArray = [...pathObjectArray.slice(0, i), ...pathObjectArray[i], ...pathObjectArray.slice(i + 1)]
                } else {
                    if (pathObjectArray[i].children instanceof Array) {
                        flag = true
                        pathObjectArray.push(pathObjectArray[i].children)
                        delete pathObjectArray[i].children
                    }
                }
            }
            if (!flag) break
        }
        return pathObjectArray
    },
    uploadFile({ url, filePath, onStarted, onChanged, onSuccessed, onFail }) {
        let cancelTokenSource;

        const startUpload = () => {
            cancelTokenSource = axios.CancelToken.source();

            const fileStream = fs.createReadStream(filePath);
            const fileSize = fs.statSync(filePath).size;

            // 上传开始回调
            if (onStarted) onStarted();

            axios.post(url, fileStream, {
                headers: {
                    'Content-Type': 'multipart/form-data'
                },
                cancelToken: cancelTokenSource.token,
                onUploadProgress: progressEvent => {
                    const percentCompleted = Math.round((progressEvent.loaded * 100) / fileSize);
                    // 上传进度更新回调
                    if (onChanged) onChanged(percentCompleted);
                }
            })
                .then(response => {
                    // 上传成功回调
                    if (onSuccessed) onSuccessed(response.data);
                })
                .catch(error => {
                    if (axios.isCancel(error)) {
                        // 上传取消回调
                        if (onFail) onFail('用户取消上传');
                    } else {
                        // 上传失败回调
                        if (onFail) onFail(error.message);
                    }
                });
        };

        const cancelUpload = () => {
            if (cancelTokenSource) {
                cancelTokenSource.cancel();
            }
        };

        // 启动上传
        startUpload();

        // 返回取消上传函数
        return cancelUpload;
    },
    genUserToken({ account }) {
        return this.encodeString(JSON.stringify({
            account,
            time: new Date()
        }))
    },
    parseUserToken({ token }) {
        try {
            return this.JSON.parse(this.decodeString(token))
        } catch (error) {
            throw 'token解析错误'
        }
    },
    check: {
        checkPassword: function (password) {
            const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,16}$/;
            return passwordRegex.test(password);
        },
        checkEmail: function (email) {
            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
            return emailRegex.test(email);
        },
        checkPhoneNumber: function (phoneNumber) {
            const phoneRegex = /^\+?[1-9]\d{1,14}$/;
            return phoneRegex.test(phoneNumber);
        },
        checkDate: function (date) {
            const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
            return dateRegex.test(date);
        },
        checkURL: function (url) {
            const urlRegex = /^(https?:\/\/)?([^\s$.?#].[^\s]*)$/i;
            return urlRegex.test(url);
        },
        checkIPAddress: function (ipAddress) {
            const ipRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
            return ipRegex.test(ipAddress);
        },
        checkIDCard: function (idCard) {
            const idCardRegex = /^\d{17}[\dXx]$/;
            return idCardRegex.test(idCard);
        },
        checkPostalCode: function (postalCode) {
            const postalCodeRegex = /^\d{6}$/;
            return postalCodeRegex.test(postalCode);
        }
    }
}

const check = {
    isArray(e) {
        return Array.isArray(e)
    },
    isInt(e) {
        return (typeof (e) == 'number') && (parseInt(e) == e)
    },
    isNumber(e) {
        return (typeof (e) == 'number')
    },
    isString(e) {
        return typeof (e) == 'string'
    },
    throwError(reason) {
        throw new Error(reason)
    }
}
